﻿// ---------------------------------------------------------------------------------------------
// <copyright file="TopologicalSorterManager.cs" company="Hiyoko">
//   Copyright (c) 2015 All Rights Reserved
// </copyright>
// <summary>
//   This class manages the topological sorting of elements.
// </summary>
// ---------------------------------------------------------------------------------------------

using Microsoft.Xrm.Sdk.Metadata;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Hiyoko.DependenciesSorter.TopologicalSort
{
    public class TopologicalSorterManager
    {
        #region Private members

        private readonly DependencyGraph _graph;

        #endregion Private members

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the TopologicalSorterManager class.
        /// </summary>
        /// <param name="entityMetadatas">The entity metadatas.</param>
        /// <param name="ignoredEntities">The list of entities to ignore.</param>
        /// <param name="ignoredDependencies">the list of dependencies to ignore.</param>
        public TopologicalSorterManager(EntityMetadata[] entityMetadatas, List<string> ignoredEntities, Dictionary<string, List<string>> ignoredDependencies)
        {
            if (entityMetadatas.Length == 0)
            {
                throw new ArgumentException("An error occured: the argument <entityMetadatas> does not contain any element.");
            }

            if (ignoredEntities == null)
            {
                ignoredEntities = new List<string>();
            }

            if (ignoredDependencies == null)
            {
                ignoredDependencies = new Dictionary<string, List<string>>();
            }

            _graph = new DependencyGraph();
            var orderedProcesses = new List<OrderedProcess>();

            var processMetadatas = entityMetadatas.Where(em => !ignoredEntities.Contains(em.LogicalName))
                                                 .ToList();

            var processNames = processMetadatas.Select(em => em.LogicalName)
                                             .ToList();

            foreach (var name in processNames)
            {
                var orderedProcess = new OrderedProcess(_graph, name);
                orderedProcesses.Add(orderedProcess);
            }

            foreach (var entityMetadata in processMetadatas)
            {
                var currentProcess = orderedProcesses.Single(p => p.Name.Equals(entityMetadata.LogicalName));
                var dependencies = entityMetadata.GetManyToOneDependencies();
                foreach (var dependency in dependencies)
                {
                    if (!processNames.Contains(dependency)
                        || currentProcess.Name.Equals(dependency)
                        || (ignoredDependencies.ContainsKey(currentProcess.Name) && ignoredDependencies[currentProcess.Name].Contains(dependency)))
                    {
                        continue;
                    }

                    var dependencyProcess = orderedProcesses.Single(p => p.Name.Equals(dependency));
                    currentProcess.After(dependencyProcess);
                }
            }
        }

        #endregion Constructor

        #region Public methods

        /// <summary>
        /// Sorts processes.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<IEnumerable<OrderedProcess>> Sort()
        {
            return _graph.CalculateSort();
        }

        #endregion Public methods
    }
}
